#include "log.h"
#include "timer.h"
#include "macro.h"


namespace qtch{
static Logger::ptr logger = QTCH_LOG_NAME("system");

bool Timer::cancel(){
    TimerManager::RWMutexType::WriteLock lock(m_manger->m_mutex);
    if(m_cb){
        m_cb = nullptr;
        auto it = m_manger->m_timers.find(shared_from_this());
        if(it != m_manger->m_timers.end()){
            m_manger->m_timers.erase(it);
        }
        return true;
    }
    return false;
}

bool Timer::reflush(){
    TimerManager::RWMutexType::WriteLock lock(m_manger->m_mutex);
    if(!m_cb){
        return false;
    }
    auto it = m_manger->m_timers.find(shared_from_this());
    if(it == m_manger->m_timers.end()){
        return false;
    }
    m_manger->m_timers.erase(it);
    next_time = GetCurrentMS() + m_ms;
    m_manger->m_timers.insert(shared_from_this());
    return true;
}

bool Timer:: reset(uint64_t ms,bool from_now){
    if(ms == m_ms && !from_now){
        QTCH_LOG_DEBUG(logger) << "------------------------";
        return true;
    }
    TimerManager::RWMutexType::WriteLock lock(m_manger->m_mutex);
    if(!m_cb){
        QTCH_LOG_DEBUG(logger) << "------------------------";
        return false;
    }
    auto it = m_manger->m_timers.find(shared_from_this());
    if(it == m_manger->m_timers.end()){
        QTCH_LOG_DEBUG(logger) << "------------------------";
        return false;
    }
    m_manger->m_timers.erase(it);
    if(from_now){
        next_time = GetCurrentMS() + ms;
    }
    else{
        next_time = next_time - m_ms + ms;
    }
    m_ms = ms;
    m_manger->m_timers.insert(shared_from_this());
    QTCH_LOG_DEBUG(logger) << "Timer::reset m_ms:" << m_ms << " m_timer.size:" <<m_manger->m_timers.size();
    return true;
}

Timer::Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager* manager)
    :m_recurring(recurring)
    ,m_ms(ms)
    ,m_cb(cb)
    ,m_manger(manager){
    next_time = GetCurrentMS() + ms;
}

Timer::Timer(uint64_t ms)
    :m_ms(ms){
    next_time = GetCurrentMS() + ms;
}

bool Timer::Comparator::operator()(const Timer::ptr & lhs,const Timer::ptr& rhs) const{

    if(lhs->next_time < rhs->next_time){
        return true;
    }
    if(lhs->next_time > rhs->next_time){
        return false;
    }
    return lhs.get() < rhs.get();
}

TimerManager::TimerManager(){

}

TimerManager::~TimerManager(){

}

Timer::ptr TimerManager::addTimer(uint64_t ms, std::function<void()> cb, bool recurring){
    Timer::ptr timer = Timer::ptr(new Timer(ms,cb,recurring,this));
    bool at_front = false;
    {
        RWMutexType::WriteLock lock(m_mutex);
        auto it = m_timers.insert(timer).first;
        bool at_front = (it==m_timers.begin()) && !m_tickle;
        if(at_front){
            m_tickle =true;
        }
    }
    if(at_front){
        onTimerInsertedAtFront();
    }
    return timer;
}

static void OnTimer(std::weak_ptr<void> weak_cond,std::function<void()> cb){
    if(!weak_cond.expired()){
        cb();
    }
}

Timer::ptr TimerManager::addConditionTimer(uint64_t ms, std::function<void()> cb,
                    std::weak_ptr<void> weak_cond, bool recurring){
    return addTimer(ms,std::bind(&OnTimer,weak_cond,cb),recurring);

}
uint64_t TimerManager::getNextTime(){
    RWMutexType::ReadLock lock(m_mutex);
    m_tickle = false;
    if(m_timers.empty()){
        return ~0ull;
    }
    const Timer::ptr& next = *m_timers.begin();
    uint64_t now_ms = GetCurrentMS();
    if(now_ms >= next->next_time){
        return 0;
    }
    else{
        return next->next_time - now_ms;
    }

}

void TimerManager::listExpectTimer(std::vector<std::function<void()> >& cbs){
    {
        RWMutexType::ReadLock lock(m_mutex);
        if(m_timers.empty()){
            return;
        }
    }
    uint64_t now_ms = GetCurrentMS();
    RWMutexType::WriteLock lock(m_mutex);
    Timer::ptr now_timer(new Timer(now_ms));
    auto it = m_timers.begin();
    for(;it != m_timers.end(); ++it){
        if((*it)->next_time <= now_ms){
            cbs.push_back((*it)->m_cb);
        }
        else{
            break;
        }
    }
    std::vector<Timer::ptr> expired;
    expired.insert(expired.begin(),m_timers.begin(),it);
    m_timers.erase(m_timers.begin(),it);
    for(auto &timer :expired){
        if(timer->m_recurring){
            timer->next_time = now_ms + timer->m_ms;
            m_timers.insert(timer);
        }
        else{
            timer->m_cb = nullptr;
        }
    }
}

bool TimerManager::hasNextTimer(){
    if(m_timers.empty()){
        return false;
    }
    return true;
}

void TimerManager::delAllTimer() {
    RWMutexType::WriteLock lock(m_mutex);
    m_timers.clear();

}

}